/* $ZEL: sis1100_open.c,v 1.7 2005/07/07 14:15:34 wuestner Exp $ */

/*
 * Copyright (c) 2001-2004  Peter Wuestner.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "sis1100_sc.h"

static void
_sis1100_open(struct sis1100_softc* sc, struct sis1100_fdata* fd, int idx)
{
    SEM_LOCK(sc->sem_fdata_list);
    list_add(&fd->list, &sc->fdata_list_head);
    SEM_UNLOCK(sc->sem_fdata_list);

    fd->fifo_mode=0;          /* can't be changed for sdram and sharc */
    fd->vmespace_am=9;        /* useless for sdram and sharc */
    fd->vmespace_datasize=4;  /* useless for sdram and sharc */
    fd->last_prot_err=0;
    fd->sig=0;
    fd->owned_irqs=0;         /* useless for sdram and sharc */
    fd->mindmalen_r=24;
    fd->mindmalen_w=400;
    fd->mmapdma.valid=0;
    sc->fdatalist[idx]=fd;
}

#ifdef __NetBSD__
int
sis1100_open(dev_t dev, int flag, int mode, struct proc *p)
{
    struct sis1100_softc* sc;
    struct sis1100_fdata* fd;
    unsigned int _minor=minor(dev);
    unsigned int card=(_minor&sis1100_MINORCARDMASK)>>sis1100_MINORCARDSHIFT;
    unsigned int subdev=(_minor&sis1100_MINORTYPEMASK)>>sis1100_MINORTYPESHIFT;
    unsigned int idx=_minor&(sis1100_MINORUTMASK);

    if (card >= sis1100cfdriver.cd_ndevs || !sis1100cfdriver.cd_devs[card]) {
        pINFOsc("open: _minor=%d card=%d idx=%d, returning ENXIO",
                _minor, card, idx);
        return ENXIO;
    }

    sc=SIS1100SC(dev);

    if (sc->fdatalist[idx])
        return EBUSY;

    fd=malloc(sizeof(struct sis1100_fdata), M_DEVBUF, M_WAITOK);
    if (!fd) return ENOMEM;
    fd->p=p;

    fd->sig=0;
    fd->subdev=subdev;

    simple_lock(&sc->lock_sc_inuse);
    sc->sc_inuse++;
    simple_unlock(&sc->sc_inuse);

    sc->fdatalist[idx]=fd;
    _sis1100_open(sc, fd, idx);

    return 0;
}

#elif __linux__

int
sis1100_open(struct inode *inode, struct file *file)
{
    struct sis1100_softc* sc;
    struct sis1100_fdata* fd;
    unsigned int _minor=iminor(inode);
    unsigned int card=(_minor&sis1100_MINORCARDMASK)>>sis1100_MINORCARDSHIFT;
    unsigned int subdev=(_minor&sis1100_MINORTYPEMASK)>>sis1100_MINORTYPESHIFT;
    unsigned int idx=_minor&sis1100_MINORUTMASK;

    if (card >= sis1100_MAXCARDS || !sis1100_devdata[card]) {
        printk(KERN_INFO "sis1100 open: returning ENXIO\n");
        return -ENXIO; /*ENODEV*/
    }
    sc=sis1100_devdata[card];

    if (sc->fdatalist[idx]) {
        return -EBUSY;
    }

    fd=kmalloc(sizeof(struct sis1100_fdata), GFP_KERNEL);
    if (!fd) return -ENOMEM;
    fd->sc=sc;
    fd->subdev=subdev;
    file->private_data = fd;

    _sis1100_open(sc, fd, idx);

    return 0;
}
#endif

static void
_sis1100_close(struct sis1100_softc* sc, struct sis1100_fdata* fd, int idx)
{
    u_int32_t mask;

    switch (sc->remote_hw) {
    case sis1100_hw_vme:
        if (fd->owned_irqs & SIS3100_IRQS) {
            sis3100writeremreg(sc, vme_irq_sc,
                    (fd->owned_irqs & SIS3100_IRQS)<<16, 0);
        }
        break;
    case sis1100_hw_camac:
        if (fd->owned_irqs & SIS5100_IRQS) {
            /*sis5100writeremreg(sc, vme_irq_sc,
                    (fd->owned_irqs & SIS5100_IRQS)<<16, 0);*/
        }
        break;
    case sis1100_hw_pci: break; /* do nothing */
    case sis1100_hw_lvd: break; /* do nothing */
    case sis1100_hw_invalid: break; /* do nothing */
    }

    if ((sc->demand_dma.status!=dma_invalid) &&
                (sc->demand_dma.owner==fd)) {
        if (sc->demand_dma.status==dma_running)
            sis1100_ddma_stop(sc, fd, 0);
        sis1100_ddma_map(sc, fd, 0);
    }

    mask=0;
    if (fd->owned_irqs & SIS1100_FRONT_IRQS) {
        mask|=(fd->owned_irqs & SIS1100_FRONT_IRQS)>>4;
    }
    if (fd->owned_irqs & SIS1100_MBX0_IRQ) {
        mask|=irq_mbx0;
    }
    if (mask) sis1100_disable_irq(sc, 0, mask);

    if (fd->mmapdma.valid)
        sis1100_dma_free(sc, fd, 0);

    SEM_LOCK(sc->sem_fdata_list);
    list_del(&fd->list);
    SEM_UNLOCK(sc->sem_fdata_list);
    sc->fdatalist[idx]=0;
}

#ifdef __NetBSD__
int
sis1100_close(dev_t dev, int flag, int mode, struct proc *p)
{
    struct sis1100_softc* sc=SIS1100SC(dev);
    struct sis1100_fdata* fd=SIS1100FD(dev);
    unsigned int minor=minor(dev);
    unsigned int idx=minor&(sis1100_MINORUTMASK);


    _sis1100_close(sc, fd, idx);

    free(fd, M_DEVBUF);
    simple_lock(&sc->lock_sc_inuse);
    sc->sc_inuse--;
    simple_unlock(&sc->sc_inuse);
    return 0;
}
#elif __linux__
int
sis1100_release(struct inode *inode, struct file *file)
{
    struct sis1100_softc* sc=SIS1100SC(file);
    struct sis1100_fdata* fd=SIS1100FD(file);
    unsigned int _minor=iminor(inode);
    unsigned int idx=_minor&(sis1100_MINORUTMASK);

    _sis1100_close(sc, fd, idx);

    kfree(fd);
    return 0;
}
#endif
